package dev.marsh.sniper;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import cn.bingoogolapple.photopicker.activity.BGAPhotoPickerActivity;
import dev.marsh.sniper.ar.tracker.JavaDetectionTracker;
import dev.marsh.sniper.core.BuildConfig;
import dev.marsh.sniper.core.R;
import dev.marsh.sniper.core.SniperUtil;
import dev.marsh.sniper.core.SniperView;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

public abstract class SniperActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2/*, SensorEventListener*/ {
    private static final String TAG = "SniperActivity";
    private static final int REQUEST_CODE_CHOOSE_PIC_FROM_GALLERY = 666;

    private SniperView mSniperView;
    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    if (BuildConfig.DEBUG) {
                        Log.i(TAG, "OpenCV loaded successfully");
                    }
                    mSniperView.startSniper();
                    if (mTracker == null) {
                        initSampleData();
                    }
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

    private JavaDetectionTracker mTracker;

    private void initSampleData() {
        Observable.just(0)
                .map(integer -> new JavaDetectionTracker(getApplicationContext()))
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())

                .subscribe(next -> {
                    if (BuildConfig.DEBUG) {
                        Log.d(TAG, next.toString());
                    }
                    mTracker = next;
                }, error -> {
                    if (BuildConfig.DEBUG) {
                        error.printStackTrace();
                    }
                });
    }

    private CameraFrameListener mListener;

    /*private SensorManager mSensorManager;
    private Sensor mSensor;
    private boolean mReady;

    private int mX, mY, mZ;
    private long mLastStaticStamp = 0;
    private Calendar mCalendar;
    public static final int DELAY_DURATION = 500;
    public static final int STATUS_NONE = 0;
    public static final int STATUS_STATIC = 1;
    public static final int STATUS_MOVE = 2;
    private int STATUE = STATUS_NONE;*/

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sniper);
        SniperUtil.setStatusBarColor(this, getResources().getColor(R.color.colorPrimary));
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        ((TextView) findViewById(R.id.title)).setText(R.string.lib_name);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayShowTitleEnabled(false);
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        mSniperView = (SniperView) findViewById(R.id.go_sniper);
        mSniperView.setDelegate(this);

        //mSensorManager = (SensorManager) getSystemService(Activity.SENSOR_SERVICE);
        //mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        initProcess();
    }

    private void initProcess() {
        Observable.create((ObservableOnSubscribe<Mat>) e -> {
            if (mListener == null) {
                mListener = e::onNext;
            }
        })
                .map(new Function<Mat, Boolean>() {
                    //int count = 0;
                    int real = 0;

                    @Override
                    public Boolean apply(Mat mat) throws Exception {
                        if (mTracker == null) {
                            return false;
                        }

                        /*if (!mReady) {
                            return false;
                        }*/

                        //if (count % 5 == 0) {
                            Mat clone = mat.clone();
                            Mat clone2 = mat.clone();
                            boolean track = mTracker.track(mat);
                            if (!track) {
                                Core.flip(clone, clone, 1);
                                track = mTracker.track(clone);
                            }
                            if (!track) {
                                Core.flip(clone2, clone2, -1);
                                track = mTracker.track(clone2);
                            }
                            if (track) {
                                real++;
                                if (real >= 10) {
                                    return true;
                                }
                            } else {
                                real = 0;
                            }
                        //}

                        /*if (count == Integer.MAX_VALUE) {
                            count = 0;
                        }
                        count++;*/

                        return false;
                    }
                })
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Boolean>() {
                    private Disposable mDisposable;

                    @Override
                    public void onSubscribe(Disposable d) {
                        mDisposable = d;
                    }

                    @Override
                    public void onNext(Boolean value) {
                        /*if (value) {
                            mDisposable.dispose();
                            showToast(true);
                        }*/
                        showToast(0, value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (BuildConfig.DEBUG) {
                            e.printStackTrace();
                        }
                        showToast(0, false, e);
                    }

                    @Override
                    public void onComplete() {
                        // nothing...
                    }
                });
    }

    @Override protected void onStart() {
        super.onStart();
        /*restParams();
        mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);*/
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            }
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mLoaderCallback);
        } else {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "OpenCV library found inside package. Using it!");
            }
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    @Override
    protected void onPause() {
        if (mSniperView != null) {
            mSniperView.stopSniper();
        }
        super.onPause();
    }

    @Override protected void onStop() {
        //mSensorManager.unregisterListener(this, mSensor);
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        if (mSniperView != null) {
            mSniperView.onDestroy();
        }
        super.onDestroy();
    }

    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.ar) {
            // nothing...
        } else if (id == R.id.photo) {
            startActivityForResult(BGAPhotoPickerActivity.newIntent(this, null, 1, null, false), REQUEST_CODE_CHOOSE_PIC_FROM_GALLERY);
        }
    }

    /*@Override public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.menu_share, menu);
        return true;
    }*/

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            finish();
            return true;
        } else if (id == R.id.menu_share) {
            doShare();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onCameraViewStarted(int width, int height) {

    }

    @Override
    public void onCameraViewStopped() {

    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        if (mListener != null && isEnable()) {
            mListener.onCameraFrame(inputFrame.gray());
        }

        return null;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_CHOOSE_PIC_FROM_GALLERY) {
            final String picturePath = BGAPhotoPickerActivity.getSelectedImages(data).get(0);

            Observable.just(0)
                    .map(integer -> {
                        Bitmap bitmap = getDecodeAbleBitmap(picturePath);
                        if (bitmap == null) {
                            return false;
                        }

                        Mat mat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);
                        Utils.bitmapToMat(bitmap, mat);
                        Mat gray = new Mat();
                        Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY, 1);

                        if (mTracker != null) {
                            return mTracker.track(gray, JavaDetectionTracker.TYPE_PHOTO);
                        }
                        return false;
                    })
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(success -> showToast(1, success),
                               error -> {
                                   if (BuildConfig.DEBUG) {
                                       error.printStackTrace();
                                   }
                                   showToast(1, false, error);
                               });
        }
    }

    /**
     * 将本地图片文件转换成可解码的Bitmap
     */
    private static Bitmap getDecodeAbleBitmap(String picturePath) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(picturePath, options);
            int sampleSize = options.outHeight / 400;
            if (sampleSize <= 0) {
                sampleSize = 1;
            }
            options.inSampleSize = sampleSize;
            options.inJustDecodeBounds = false;

            return BitmapFactory.decodeFile(picturePath, options);
        } catch (Exception e) {
            return null;
        }
    }

    private void showToast(int type, boolean success) {
        showToast(type, success, null);
    }

    private void showToast(int type, boolean success, Throwable e) {
        //Toast.makeText(getApplicationContext(), success ? R.string.sniper_success : R.string.sniper_failure, Toast.LENGTH_SHORT).show();
        if (success) {
            vibrate();
        } else {
            if (e != null) {
                //Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }

        if (type == 0) {
            result(success);
        } else {
            photoResult(success);
        }
    }

    private void vibrate() {
        Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        vibrator.vibrate(200);
    }

    public interface CameraFrameListener {
        void onCameraFrame(Mat mat);
    }

    /*@Override public void onSensorChanged(SensorEvent event) {
        if (event.sensor == null) {
            return;
        }

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            int x = (int) event.values[0];
            int y = (int) event.values[1];
            int z = (int) event.values[2];
            mCalendar = Calendar.getInstance();
            long stamp = mCalendar.getTimeInMillis();

            if (STATUE != STATUS_NONE) {
                int px = Math.abs(mX - x);
                int py = Math.abs(mY - y);
                int pz = Math.abs(mZ - z);

                double value = Math.sqrt(px * px + py * py + pz * pz);
                if (value > 1.4) {
                    STATUE = STATUS_MOVE;
                    mReady = false;
                } else {
                    if (STATUE == STATUS_MOVE) {
                        mLastStaticStamp = stamp;
                    }

                    STATUE = STATUS_STATIC;
                    mReady = stamp - mLastStaticStamp > DELAY_DURATION;
                }
            } else {
                mLastStaticStamp = stamp;
                STATUE = STATUS_STATIC;
                mReady = false;
            }

            mX = x;
            mY = y;
            mZ = z;
        }
    }

    private void restParams() {
        STATUE = STATUS_NONE;
        mReady = false;
        mX = 0;
        mY = 0;
        mZ = 0;
    }

    @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // nothing...
    }*/

    protected abstract void result(boolean success);

    protected abstract void photoResult(boolean success);

    protected abstract void doShare();

    private boolean mEnable = true;

    public boolean isEnable() {
        return mEnable;
    }

    public void setEnable(boolean enable) {
        this.mEnable = enable;
    }
}
